﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using System.Linq.Expressions;
using Legend.Fakes.Extensibility;
using System.IO;
using System.Diagnostics;

namespace Legend.Fakes.Tests
{
    [TestFixture]
    public class ItTess 
    {
        [Test]
        public void Calls_should_match_where_argument_validator_validates_argument(
            [Values("foo", "bar", "baz", "", null)] string argumentValue)
        {
            var foo = A.Fake<IFoo<string>>();

            foo.Configure().CallsTo(x => x.Bar(Argument.Is.Any<string>())).Returns(true);

            Assert.That(foo.Bar(argumentValue), Is.True);
        }


        [Test]
        public void Calls_should_not_match_where_argument_validator_does_not_validate_argument(
            [Values("foo", "bar", "baz", "", null)] string argumentValue)
        {
            var foo = A.Fake<IFoo<string>>();

            foo.Configure().CallsTo(x => x.Bar(Argument.Is.Match<string>(s => false))).Returns(true);

            Assert.That(foo.Bar(argumentValue), Is.False);
        }

        [Test]
        public void Methods_that_are_not_tagged_with_the_ArgumentValidatorAttribute_should_be_invoked()
        {
            var foo = A.Fake<IFoo<string>>();

            foo.Configure().CallsTo(x => x.Bar(Argument.Is.NoValidator("foo"))).Returns(true);
            foo.Configure().CallsTo(x => x.Bar(Argument.Is.Any<string>())).Returns(false);            

            Assert.That(foo.Bar("foo"), Is.True);
            Assert.That(foo.Bar("bar"), Is.False);
        }

        [Test]
        public void Validation_should_throw_when_parameters_to_it_extension_does_not_match_constructor_parameters_for_validator()
        {
            var fake = A.Fake<IFormattable>();

            fake.Configure().CallsTo(x => x.ToString(Argument.Is.ArgumentMismatchBetweenExtensionAndValidator<string>("", 0), Argument.Is.Any<IFormatProvider>()));

            Assert.Throws<ArgumentValidationException>(() => 
                fake.ToString("", A.Fake<IFormatProvider>()));
        }

        [Test]
        public void Validation_should_throw_when_type_parameters_to_it_extension_does_not_match_type_parameters_to_generic_validator_type()
        {
            var fake = A.Fake<IFormattable>();

            fake.Configure().CallsTo(x => x.ToString(Argument.Is.TypeArgumentMismatchBetweenExtensionAndValidator(), Argument.Is.Any<IFormatProvider>())).Returns("foo");

            Assert.Throws<ArgumentValidationException>(() =>
                fake.ToString("", A.Fake<IFormatProvider>()));
        }

        public interface IFoo<T>
        {
            bool Bar(T baz);
        }

        private Expression GetFirstArgumentAsExpression(Expression<Action> call)
        {
            var methodCall = call.Body as MethodCallExpression;
            
            return methodCall.Arguments.First();
        }

        private void Foo(string s)
        {
            throw new NotImplementedException();
        }
    }

    public static class IsExtensions
    {
        public static string NoValidator(this IExtensibleIs extensionPoint, string returnValue)
        {
            return returnValue;
        }

        [ArgumentValidator(typeof(LongerThanStringArgumentValidator))]
        public static string LongerThan(this IExtensibleIs extensionPoint, int length)
        {
            throw new NotImplementedException();
        }

        
        [ArgumentValidator(typeof(ValidatorThatTakesOneStringInTheConstructor))]
        public static T ArgumentMismatchBetweenExtensionAndValidator<T>(this IExtensibleIs extensionPoint, string anArgument, int anotherArgument)
        {
            throw new NotImplementedException();
        }

        
        public class ValidatorThatTakesOneStringInTheConstructor
            : IArgumentValidator
        {
            public ValidatorThatTakesOneStringInTheConstructor(string foo)
            { 
            
            }

            #region IArgumentValidator Members

            public bool IsValid(object argument)
            {
                throw new NotImplementedException();
            }

            #endregion
        }

        [ArgumentValidator(typeof(ValidatorThatTakesATypeArgument<>))]
        public static string TypeArgumentMismatchBetweenExtensionAndValidator(this IExtensibleIs extensionPoint)
        {
            throw new NotImplementedException();
        }

        private class ValidatorThatTakesATypeArgument<T>
            : IArgumentValidator
        {

            #region IArgumentValidator Members

            public bool IsValid(object argument)
            {
                throw new NotImplementedException();
            }

            #endregion
        }


        public class LongerThanStringArgumentValidator
            : IArgumentValidator
        {
            public int lowerBound;

            public LongerThanStringArgumentValidator(int lowerBound)
            {
                this.lowerBound = lowerBound;
            }

            public bool IsValid(string argument)
            {
                return argument.Length > this.lowerBound;
            }

            bool IArgumentValidator.IsValid(object argument)
            {
                return this.IsValid((string)argument);
            }
        }

    }
}
